#ifndef LMODEL_H
#define LMODEL_H
#include <matrix.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <time.h>
#include <math.h>

using namespace std;

template <class TipoClasse>
class lmodel
{
    matrix<TipoClasse> u, y, A, fi, modelCoef;
    int nu, ny, ne;
    string TypeModel;

public:
    lmodel();
    void addData(TipoClasse *in, TipoClasse  *out);
    void addData(string in, string out);
    // Modelos polinomiais
    void polyModel(TipoClasse *in, TipoClasse  *out, int degree);
    void polyModel(string in, string  out, int degree);
    void polyModel(TipoClasse in, int degree);
    // simulação de dos modelos
    void addModelCoef(string in);
    matrix<TipoClasse> sim(TipoClasse in, int degree);

    void print();
};

template <class TipoClasse>
lmodel<TipoClasse>::lmodel()
{
}

template <class TipoClasse>
void lmodel<TipoClasse>::addData(TipoClasse *in, TipoClasse *out)
{
    this->u = in;
    this->y = out;
}

template <class TipoClasse>
void lmodel<TipoClasse>::addData(string in, string out)
{
    this->u = in;
    this->y = out;
}

template <class TipoClasse>
void lmodel<TipoClasse>::addModelCoef(string in)
{
    int positionSemiComma = 0, contRow = 1;
    string tempIn = in, temp;
    while(true)
    {
        positionSemiComma = tempIn.find(";");
        if(positionSemiComma != -1)
        {
            string temp = tempIn.substr(0,positionSemiComma);
            TipoClasse Itemp = (TipoClasse) atof(temp.c_str());
            this->modelCoef.add(contRow, 1, Itemp);
            tempIn.erase(0, positionSemiComma + 1);
            contRow++;
        }
        else
            break;
    }
    TipoClasse Itemp = (TipoClasse) atof(tempIn.c_str());
    this->modelCoef.add(contRow, 1, Itemp);
    this->modelCoef.print();
}

template <class TipoClasse>
void lmodel<TipoClasse>::polyModel(TipoClasse *in, TipoClasse *out, int degree)
{
    this->addData(in,out);
    this->TypeModel = "polinomial";

    if(this->y.lin < this->y.col)
        this->y = ~y;

    if(this->u.lin < this->u.col)
        this->u = ~u;

    for(int i = 0; i < degree + 1; i++)
    {
        A = (A&(u^=i));
    }

}



template <class TipoClasse>
void lmodel<TipoClasse>::polyModel(TipoClasse in, int degree)
{
    this->TypeModel = "polinomial";
    fi.init(1,degree+1);
    for(int i = 0; i < degree + 1; i++)
    {
        fi.Mat[0][i] = pow(in, i);
    }

    fi.print();

}

template <class TipoClasse>
void lmodel<TipoClasse>::polyModel(string in, string out, int degree)
{
    this->addData(in,out);
    this->TypeModel = "polinomial";

    if(this->y.lin < this->y.col)
        this->y = ~y;

    if(this->u.lin < this->u.col)
        this->u = ~u;

    for(int i = 0; i < degree + 1; i++)
    {
        A = (A&(u^=i));
    }

}

template <class TipoClasse>
matrix<TipoClasse> lmodel<TipoClasse>::sim(TipoClasse in, int degree)
{
    matrix<TipoClasse> ret, out;
    if(this->TypeModel == "polinomial")
        this->polyModel(in,degree);
    ret = this->fi;
    out = this->fi * this->modelCoef;
    return out;
}

template <class TipoClasse>
void lmodel<TipoClasse>::print()
{
    for(int i = 0; i < this->A.lin; i++)
    {
        for(int j = 0; j < this->A.col; j++)
        {
        cout << this->A.Mat[i][j] <<"  ";
        }

        if(i == round(this->A.lin/2))
            cout <<" = ";
        else
            cout <<"   ";

        for(int j = 0; j < this->y.col; j++)
        {
        cout << this->y.Mat[i][j] <<"  ";
        }

        cout << endl;
    }
}


#endif // LMODEL_H
